import socket
import select
# step , generate socket object
from threading import Thread

# 创建并配置套接口对象
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# bind to address info
server.bind(("10.2.0.243", 7777))
clients = []
# listen
server.listen(10)
print("The server is running ......")

users_to_socks = {}
socks_to_users = {}
data_queue = {}

def do_cmd(client, data):
    cmd,obj = data.split(":", 1)
    if cmd == "login":
        users_to_socks[obj] = client
        socks_to_users[client] = obj
    elif cmd == "message":
        user, message = obj.split(":")
        receiver = user
        sender = socks_to_users[client]
        receiver_sock = users_to_socks[receiver]

        # 将要发送的数据追加到队列
        if receiver_sock not in data_queue:
            data_queue[receiver_sock] = []
        data_queue[receiver_sock].append("message:{}:{}".format(sender, message).encode("utf-8"))
        global write_list
        if receiver_sock not in write_list:
            write_list.append(receiver_sock)


read_list = []
write_list = []
except_list = []

read_list.append(server)
while True:
    # 使用select等待多个socket对象的read/write IO发生
    ready_to_read, ready_to_write, ready_to_except = select.select(read_list, write_list, except_list)

    # 检测可读列表
    for sock in ready_to_read:
        if sock == server:
            # 如果有客服端连接，则接受
            client, addr = server.accept()
            read_list.append(client)
            continue
        else:
            # 如果客户端发送数据
            try:
                data = sock.recv(65535)
                if not data:
                    raise Exception
                do_cmd(sock, data.decode("utf-8"))
            except Exception:
                # 如果客户端断开，对资源进行回收
                read_list.remove(sock)
                if sock in write_list:
                    write_list.remove(sock)

                if sock in socks_to_users:
                    user = socks_to_users[sock]
                    print("{} quited".format(user))
                    del socks_to_users[sock]
                    if user in users_to_socks:
                        del users_to_socks[user]
                
                sock.close()
                continue
    # socket io发生可写事件
    for sock in ready_to_write:
        # 找到要发向该套接口的数据队列。
        datas = data_queue[sock]
        # 取出最先要发送的数据
        data = datas.pop(0)
        sock.send(data)
        # 如果所有数据发送完成则将该套接口从“写”列表移除
        if not datas:
            write_list.remove(sock)

    for sock in ready_to_except:
        pass

server.close()
